package cn.com.sinux.spring.repositories;

import cn.com.sinux.spring.utils.BeanUtils;
import cn.com.sinux.spring.utils.PageUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import java.io.Serializable;
import java.util.List;

/**
 * Created:14-7-3 上午10:06
 *
 * @author <a href="mailto:caolei@sinosoft.co"> caolei </a>
 * @version 1.0
 */

public class BaseRepository<T, ID extends Serializable>
        extends SimpleJpaRepository<T, ID> implements IBaseRepository<T, ID> {
    @SuppressWarnings("unused")
    private EntityManager entityManager;
    private JpaEntityInformation<T, ?> entityInformation;

    public BaseRepository(Class<T> domainClass, EntityManager entityManager) {
        super(domainClass, entityManager);
//        this.entityInformation = JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager);
        this.entityManager = entityManager;
    }

    public BaseRepository(final JpaEntityInformation<T, ?> entityInformation,
                          final EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityManager = entityManager;
    }


    public Page<T> findAll(Iterable<ID> ids, Pageable pageable) {
        final Iterable<ID> _ids = ids;
        if (_ids == null || !_ids.iterator().hasNext()) {
            return PageUtils.emptyPage(pageable);
        }
        final Specification<T> specification = new Specification<T>() {

            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Path<?> path = root.get(entityInformation.getIdAttribute());
                query.where(path.in(_ids));
                return query.getRestriction();
            }
        };
        TypedQuery<T> query = getQuery(specification, pageable);
//        query.setParameter(specification.parameter, ids);
//        specification.parameter.in(ids);
        return pageable == null ? new PageImpl<T>(query.getResultList()) : readPage(query, pageable, specification);
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
     */
    @Transactional
    public <S extends T> void saveByIgnoreProperties(S entity, Specification<T> specification, String... ignoreProperties) {
        List<T> list = findAll(specification);
        for (T t : list) {
            BeanUtils.copyProperties(entity, t, ignoreProperties);
        }
        if (!list.isEmpty()) {
            save(list);
        }
    }


    /*
 * (non-Javadoc)
 * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
 */
    @Transactional
    public <S extends T> void saveByContainProperties(S entity, Specification<T> specification, String... containProperties) {
        List<T> list = findAll(specification);
        for (T t : list) {
            BeanUtils.copyPropertiesByContain(entity, t, containProperties);
        }
        if (!list.isEmpty()) {
            save(list);
        }

    }

    /*
 * (non-Javadoc)
 * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
 */
    @Transactional
    public <S extends T> void save(S entity, Specification<T> specification, boolean ignoreNull) {
        List<T> list = findAll(specification);
        for (T t : list) {
            BeanUtils.copyProperties(entity, t, ignoreNull);
        }
        if (!list.isEmpty()) {
            save(list);
        }
    }


    /*
 * (non-Javadoc)
 * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
 */
    @Transactional
    public <S extends T> void save(S entity, ID key, boolean ignoreNull) {
        T one = findOne(key);
        BeanUtils.copyProperties(entity, one, ignoreNull);
        save(one);
    }

//    private final class ByIdsSpecification implements Specification<T> {
//
//        ParameterExpression<Iterable> parameter;
//
//        /*
//         * (non-Javadoc)
//         * @see org.springframework.data.jpa.domain.Specification#toPredicate(javax.persistence.criteria.Root, javax.persistence.criteria.CriteriaQuery, javax.persistence.criteria.CriteriaBuilder)
//         */
//        public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//
//            Path<?> path = root.get(entityInformation.getIdAttribute());
//            parameter = cb.parameter(Iterable.class);
//            return path.in(parameter);
//        }
//    }
}
